home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / Snippets / Printing / HairLines / HairLines.p < prev    next >
Encoding:
Text File  |  1992-07-15  |  18.0 KB  |  669 lines  |  [TEXT/MPS ]

  1. {**
  2.  **     Program: HairLines
  3.  **
  4.  **     Version: 1.03    6/16/91
  5.  **
  6.  **     Purpose:
  7.  **
  8.  **        HairLines demonstrates the proper way to draw hairlines on both
  9.  **        PostScript and QuickDraw printers.
  10.  **        
  11.  **        Dave Hersey
  12.  **
  13.  **        Macintosh Developer Technical Support
  14.  **
  15.  **        ------
  16.  **        v1.0    - 4/28/91, DMH
  17.  **                - First attempt.
  18.  **
  19.  **        v1.01    - 5/6/91, DMH
  20.  **                - Added missing DisposHandle call for widHdl in DrawStuff.
  21.  **                  Fixed scaling problems so that all methods scale alike.
  22.  **                  Added 'vers' and 'SIZE' resources.
  23.  **                  Adjusted SetLineWidth scale values so that true hairlines are created.
  24.  **
  25.  **        v1.02    - 5/9/91, DMH
  26.  **                - Revised comments about SetLineWidth.  The minimum value is
  27.  **                  1/128, not 1/28.
  28.  **                - Fixed the drawing routine so that it doesn't box rectangles that
  29.  **                  have collapsed on themselves.  There were logic bugs.
  30.  **
  31.  **        v1.03    - 6/16/91, DMH
  32.  **                - Revised comments throughout source code.  No actual code changes.
  33.  **
  34.  **}
  35.  
  36. PROGRAM HairLines;
  37.  
  38. USES
  39.     MemTypes, QuickDraw, OSIntf, ToolIntf, Traps, MacPrint, Packages, PrintComments;
  40.  
  41. CONST
  42.  
  43.     {The following constants are used to identify menus and their items. The menu IDs
  44.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  45.  
  46.     rMenuBar    = 128;                    {menubar}
  47.  
  48.     mApple        = 128;                    {Apple menu}
  49.     iAbout        = 1;
  50.  
  51.     mFile        = 129;                    {File menu}
  52.     iPrint        = 1;
  53.     iQuit        = 3;
  54.  
  55.     mEdit        = 130;                    {Edit menu}
  56.  
  57.     mSettings    = 131;                    {Settings menu}
  58.     iuseSetLine    = 1;
  59.     iusePrGen    = 2;
  60.     iuseScale    = 3;
  61.  
  62.  
  63. VAR
  64.  
  65.     gQuitting    : Boolean;                {"Are we all done?" flag}
  66.  
  67.  
  68. {*------ DrawStuff -----------------------------------------------------------------*}
  69.  
  70. {**
  71.  **      DrawStuff draws the objects.  If doSetLineWidth is true, we make SetLineWidth
  72.  **        PicComment calls.  The doPrGeneral flag is just passed in for the text output.
  73.  **        prRsl is the resolution of the printer's GrafPort and is used to determine
  74.  **        the amount to scale everything.
  75.  **}
  76.  
  77. {$S Main}
  78.  PROCEDURE DrawStuff(theWorld : Rect; theGPort : GrafPtr; doPrGeneral, doSetLineWidth, doScale: Boolean; prRsl : Integer);
  79.  
  80.  VAR
  81.     oldPort        : GrafPtr;
  82.     Width        : Widhdl;
  83.     aRect        : Rect;
  84.     count        : Integer;
  85.     scaleFactor    : Comp;
  86.     fNum        : Integer;
  87.     numStr        : Str255;
  88.     hInc, vInc    : Integer;
  89.     temp        : Integer;
  90.  
  91.  BEGIN
  92.  
  93. {Get the current port and save it.  Set the port to our drawing GrafPort and clip
  94.  it to the world's bounds.  Finally, make sure we're using normal pen settings.}
  95.  
  96.     GetPort(oldPort);
  97.     SetPort(theGPort);
  98.     ClipRect(theWorld);
  99.     PenNormal;
  100.  
  101.  
  102. {If we're supposed to be using the SetLineWidth PicComment, then use it.  The
  103.  pen size is already set to (1, 1) by the PenNormal call above.  When this prints
  104.  it will be treated as a 4 pixel pen size.  By passing a pen size scalar with the
  105.  SetLineWidth PicComment, we can make this smaller.  The smallest scalar that 
  106.  SetLineWidth recognizes is 1/128th.  Anything smaller may cause problems.  In
  107.  practice, values from 1/26 to 1/128 yield the same results.  The SetLineWidth
  108.  PicComment is currently only supported by Postscript printer drivers, and is
  109.  explained in Tech Note #91.}
  110.  
  111.     Width := widhdl(NewHandle(sizeof(widpt)));
  112.  
  113.     Width^^.h := 28;                {denominator.  1/128th is max. value of scalar,
  114.                                       but 1/28 yields the same results on a standard
  115.                                      LaserWriter.}
  116.  
  117.     Width^^.v := Round(prRsl/72);    {numerator.  If we're not at 72 dpi, we need
  118.                                      to adjust the penSize scalar for the printer's
  119.                                      coordinate system.}        
  120.  
  121.     IF (doSetLineWidth) THEN PicComment(SetLineWidth, 4, Handle(width));
  122.  
  123.  
  124. {We need to make sure that our graphics are scaled correctly for the printer's
  125.  resolution.  They were created at 72 dpi, (the standard), and need to be scaled
  126.  based on the printer port's resolution.  The port defaults to 72 dpi,
  127.  but we may have used PrGeneral to increase this resolution.  If we passed
  128.  a FALSE doScale value to this routine, no scaling will be done.  This shows
  129.  the effect of using PrGeneral to set the printer to a higher resolution
  130.  without scaling.  Normally, you wouldn't want to do this.  It's just here for
  131.  demonstration purposes.}
  132.  
  133.     IF doScale THEN
  134.         scaleFactor := prRsl/72.0
  135.     ELSE
  136.         scaleFactor := 1.0;
  137.  
  138.  
  139. {Set aRect to the area we'll draw in.  In this case, it's the entire page.
  140.  Next, move the pen to the bottom of the page and set the Times font.
  141.  Notice that we need to scale the font size just as we would any other
  142.  graphic when using a non-72 dpi printer port.  If we don't, things will
  143.  be scaled wrong.}
  144.  
  145.     aRect := theWorld;
  146.  
  147.     MoveTo(aRect.left +Round(5 * scaleFactor), aRect.bottom -Round(10 * scaleFactor));
  148.     GetFNum('Times', fnum);
  149.     TextFont(fNum);
  150.     TextSize(Round(12 * scaleFactor));
  151.  
  152.  
  153. {Draw a string that explains the graphic being printed.  If non-hairlined
  154.  objects are being printed, we just say that.  Otherwise, we give other
  155.  vitals such as what methods were used and the resolution the test pattern
  156.  was printed at.}
  157.  
  158.     IF (NOT doPrGeneral AND NOT doSetLineWidth) THEN
  159.         DrawString('An example of non-hairlined objects.')
  160.     ELSE
  161.     BEGIN
  162.     
  163.         DrawString('An example of hairlined objects printed at ');
  164.  
  165.         NumToString(prRsl, numStr);
  166.         DrawString(numStr);
  167.         DrawString(' dpi ');
  168.     
  169.         IF (NOT doSetLineWidth) THEN DrawString('w/o ');
  170.             
  171.         DrawString('using the SetLineWidth PicComment, ');
  172.         
  173.         IF (NOT doPrGeneral) THEN DrawString('w/o ');
  174.     
  175.         DrawString('using PrGeneral.');
  176.  
  177.     END;
  178.  
  179.  
  180. {Draw a string saying we didn't scale if we didn't and we should have.
  181.  This way, the user knows why the page looks funny.  We scale our font
  182.  size here, no matter what doScale says.  Otherwise they won't be able
  183.  to read the message.  Also bop the top of the drawing area down a bit
  184.  so we won't draw over our message.}
  185.  
  186.     IF (NOT doScale AND doPrGeneral) THEN
  187.     BEGIN
  188.  
  189.         MoveTo(5 * Round(prRsl/72), 20 * Round(prRsl/72));
  190.         TextSize(12 * Round(prRsl/72));
  191.         DrawString('Graphics not appropriately scaled for PrGeneral.');
  192.         aRect.top := aRect.top + 30 * Round(prRsl/72);
  193.  
  194.     END;
  195.  
  196.  
  197. {Now we actually draw the objects.  This is just a bunch of lines, rectangles
  198.  and ovals that make a quasi-neat pattern.  We move the bottom of the drawable
  199.  up a tad so that we don't draw over our text.  hInc and vInc are the increments
  200.  to move in after drawing each set of objects.}
  201.  
  202.     aRect.bottom := aRect.bottom -Round(30 * scaleFactor);
  203.  
  204.     hInc := 10;
  205.     vInc := 15;
  206.  
  207.     FOR count := 1 TO 35 DO
  208.     BEGIN
  209.  
  210. {The check below tells us if the left and right of aRect have crossed for
  211.  the first time.  If so, we change our increments to expand the graphics
  212.  back outward.}
  213.  
  214.         IF (aRect.left > aRect.right) OR (aRect.top > aRect.bottom) THEN
  215.         BEGIN
  216.             
  217.             IF (aRect.top > aRect.bottom) THEN
  218.             BEGIN
  219.  
  220.                 temp := aRect.top;
  221.                 aRect.top := aRect.bottom;
  222.                 aRect.bottom := temp;
  223.                 hInc := 5;
  224.                 vInc := -9;
  225.  
  226.             END;
  227.  
  228.             IF (aRect.left > aRect.right) THEN
  229.             BEGIN
  230.  
  231.                 temp := aRect.left;
  232.                 aRect.left := aRect.right;
  233.                 aRect.right := temp;
  234.                 hInc := -5;
  235.                 vInc := 9;
  236.  
  237.             END;
  238.         END;
  239.  
  240.  
  241. {Draw the objects.  When done, dispose of our width handle and restore the old
  242.  graphics port.}
  243.  
  244.         InsetRect(aRect, Round(scaleFactor * hInc), Round(scaleFactor * vInc));
  245.  
  246.  
  247. {Draw a rectangle and an oval, but only do this if our rectangle hasn't been
  248.  inset so much that its sides have collapsed.  Otherwise, what QuickDraw and
  249.  a particular driver do will be unpredictable.}
  250.  
  251.         IF (hInc > 0) AND (vInc > 0) THEN
  252.         BEGIN
  253.  
  254.             FrameRect(aRect);
  255.             FrameOval(aRect);
  256.  
  257.         END;
  258.  
  259.         MoveTo(aRect.left +1, aRect.top +1);
  260.         LineTo(aRect.right -1, aRect.bottom -1);
  261.         MoveTo(aRect.left +1, aRect.bottom -1);
  262.         LineTo(aRect.right -1, aRect.top +1);
  263.  
  264.     END;
  265.  
  266.     IF (Width <> nil) THEN DisposHandle(Handle(Width));
  267.     SetPort(oldPort);
  268.  
  269.  END;    {**  DrawStuff  **}
  270.  
  271.  
  272. {*------ GetBestRsl -----------------------------------------------------------------*}
  273.  
  274. {**
  275.  **      GetBestRsl determines the best "square" resolution supported by the printer.
  276.  **        For example, 300 dpi horizontal by 300 dpi vertical.  It isn't necessary to
  277.  **        use square resolutions, but it generally proves easier.  We use PrGeneral and
  278.  **        the getRslDataOp opCode to get a list of the supported resolutions for our
  279.  **        printer.  Then we just go through the rgRslRec and find the maximum square
  280.  **        resolution for discrete or non-discrete data, whichever we have.  Finally, we
  281.  **        make sure it's divisible by 72 for cleaner scaling.
  282.  **}
  283.  
  284. {$S Main}
  285. FUNCTION GetBestRsl :Integer;
  286.  
  287. VAR
  288.         err            : OSErr;
  289.         theRes, num    : Integer;
  290.         getRslData    : TGetRslBlk;
  291.  
  292. BEGIN
  293.  
  294. {Start off with our maximum resolution at 0, then call PrGeneral and parse our list
  295.  of returned values.}
  296.  
  297.         theRes := 0;
  298.         getRslData.iOpCode := getRslDataOp;
  299.  
  300.         PrGeneral(@getRslData);
  301.         err := getRslData.iError;
  302.  
  303. {If our printer only supports discrete resolutions, find the largest square one and
  304.  use that.  If our printer supports a range of resolutions, choose the smaller of the
  305.  maximum X and Y resolutions, then make it divisible by 72 for cleaner scaling.}
  306.  
  307.         IF (err = noErr) THEN
  308.             IF (getRslData.XRslRg.iMax = 0) AND (getRslData.YRslRg.iMax = 0) THEN
  309.                 BEGIN                                                                {Discrete resolutions.}
  310.                     FOR num := 1 TO getRslData.iRslRecCnt DO
  311.                         IF (getRslData.rgRslRec[num].iXRsl = getRslData.rgRslRec[num].iYRsl)
  312.                             AND (theRes < getRslData.rgRslRec[num].iXRsl) THEN
  313.                                 theRes := getRslData.rgRslRec[num].iXRsl;
  314.                 END
  315.             ELSE
  316.                 BEGIN                                                                {Variable resolutions.}
  317.                     IF (getRslData.XRslRg.iMax < getRslData.YRslRg.iMax) THEN
  318.                         theRes := (getRslData.XRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. X resolution.}
  319.                     ELSE
  320.                         theRes := (getRslData.YRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. Y resolution.}
  321.                 END;
  322.             
  323.  
  324. {In the unlikely event that PrGeneral fails and theRes is still 0, set it to 72.
  325.  This most likely is a supported resolution.  Finally return the best resolution we
  326.  could find.}
  327.  
  328.         IF theRes = 0 THEN theRes := 72;
  329.         GetBestRsl := theRes;
  330.  
  331. END;    {**  GetBestRsl  **}
  332.  
  333.  
  334. {*------ PrintStuff ----------------------------------------------------------------*}
  335. {**
  336.  **        PrintStuff will call all of the necessary Print Manager calls to print 
  337.  **        a document. It checks PrError after each Print Manager call. If an error 
  338.  **     is found, all of the Print Manager open calls (i.e. PrOpen, PrOpenDoc...) 
  339.  **        will have a corresponding close call before the error is posted to the user. 
  340.  **        You want to use this approach to make sure the Print Manager closes properly 
  341.  **        and all temporary memory is released.
  342.  **}
  343.  
  344. {$S Main}
  345. PROCEDURE PrintStuff;
  346.  
  347. VAR
  348.     Loop,
  349.     NumberOfPages,
  350.     PageNumber        : Integer;
  351.     PrintError        : LongInt;
  352.     oldPort          : GrafPtr;
  353.     thePrRecHdl        : THPrint;
  354.     thePrPort        : TPPrPort;
  355.     theStatus        : TPrStatus;
  356.     errStr            : Str255;
  357.     rslData            : TSetRslBlk;
  358.     err                : OSErr;
  359.     prRsl            : Integer;
  360.     doPrGeneral        : Boolean;
  361.     hasPScript        : Boolean;
  362.     doSetLineWidth    : Boolean;
  363.     doScale            : Boolean;
  364.     setMHdl            : MenuHandle;
  365.     mark            : Char;
  366.     bestRsl            : Integer;
  367.  
  368. BEGIN
  369.  
  370. {Get our settings from the Settings menu and set up our Booleans appropriately.}
  371.  
  372.     setMHdl := GetMHandle(mSettings);
  373.     GetItemMark(setMHdl, iuseSetLine, mark);
  374.     doSetLineWidth := (mark <> Char(noMark));
  375.     GetItemMark(setMHdl, iusePrGen, mark);
  376.     doPrGeneral := (mark <> Char(noMark));
  377.     GetItemMark(setMHdl, iuseScale, mark);
  378.     doScale := (mark <> Char(noMark));
  379.  
  380.  
  381. {Get our current port and create a print handle.  If no errors,
  382.  do our PrOpen call and, if no errors again, get the default
  383.  settings for the current driver.}
  384.  
  385.     GetPort(oldPort);
  386.     thePrRecHdl := THPrint(NewHandle(sizeof(TPrint)));
  387.     
  388.     IF (MemError = noErr) THEN
  389.     BEGIN
  390.         PrOpen;
  391.         IF (PrError = noErr) THEN
  392.         BEGIN
  393.             PrintDefault(thePrRecHdl);
  394.  
  395.  
  396. {At this point we can check for PostScript drivers.  If the
  397.  high byte of the prStl dialog's wDev field is 3, we're using an Apple
  398.  PostScript driver.  Most third party drivers use this notation as well.
  399.  DTS does not recommend using this method unless you absolutely need to,
  400.  although it is a low-risk compatibility problem.  In other words, it's
  401.  the only way to check for PostScript now, but some day it will no
  402.  longer work.}
  403.  
  404.             hasPScript := BAND(thePrRecHdl^^.prStl.wDev, $0300) = $0300;
  405.  
  406.  
  407. {If this printer driver does not support PostScript, clear our SetLineWidth
  408.  flag.  This PicComment is currently only supported with PostScript drivers.}
  409.  
  410.             IF (NOT hasPScript) THEN
  411.                 doSetLineWidth := FALSE;
  412.  
  413.  
  414. {Set prRsl to 72 in case we aren't using PrGeneral or we get an error
  415.  when setting the best resolution.  If we are using PrGeneral, get the
  416.  best "square" resolution and set the printer to that.  If we have a
  417.  problem, we'll use a resolution of 72 dpi, which is the default for
  418.  most Macintosh printers.}
  419.  
  420.             prRsl := 72;
  421.  
  422.             IF (doPrGeneral) THEN
  423.             BEGIN
  424.  
  425.                 bestRsl := GetBestRsl;
  426.                 rslData.iOpCode := SetRslOp;
  427.                 rslData.hPrint := thePrRecHdl;
  428.                 rslData.iXRsl := bestRsl;
  429.                 rslData.iYRsl := bestRsl;
  430.                 PrGeneral(@rslData);
  431.                 err := rslData.iError;
  432.  
  433.                 IF (err = noErr) THEN prRsl := bestRsl;
  434.  
  435.             END;
  436.  
  437.  
  438. {If we still have no errors, give style and print job dialogs, then open a
  439.  document and its page.  Keep checking for those dang printer errors.}
  440.  
  441.             IF (PrError = noErr) THEN
  442.             BEGIN
  443.                 IF (PrStlDialog(thePrRecHdl)) THEN
  444.                 BEGIN
  445.                     IF (PrJobDialog(thePrRecHdl)) THEN 
  446.                     BEGIN
  447.                         thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
  448.                                
  449.                         IF (PrError = noErr) THEN
  450.                         BEGIN
  451.                             PrOpenPage(thePrPort, NIL);
  452.  
  453.  
  454. {If we're still running error-free, draw our test page.  rPage is the
  455.  printed page's dimensions, thePrPort is the printer port we're drawing
  456.  into, doPrGeneral, doSetLineWidth and doScale are flags relating to our
  457.  printing methods and prRsl is the resolution of our printer port.}
  458.  
  459.                             IF (PrError = noErr) THEN
  460.                                 DrawStuff(thePrRecHdl^^.prInfo.rPage, 
  461.                                           GrafPtr(thePrPort),
  462.                                           doPrGeneral, doSetLineWidth, doScale, prRsl);
  463.  
  464.  
  465. {When done, close our page and document and spool the document if necessary.  When
  466.  finshed, call PrClose to end the whole shabang.}
  467.  
  468.                             PrClosePage(thePrPort);
  469.                         END;
  470.                              
  471.                         PrCloseDoc(thePrPort);
  472.                              
  473.                         IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) THEN
  474.                             PrPicFile(thePrRecHdl, NIL, NIL, NIL, theStatus);
  475.                     END;
  476.                 END;
  477.             END;
  478.         END;
  479.         
  480.         PrClose;
  481.  
  482.     END;
  483.  
  484. END;    {**  PrintStuff  **}
  485.  
  486.  
  487. {*------ Initialize ----------------------------------------------------------------*}
  488. {**
  489.  **        Initialize just handles necessary Toolbox initializing, setting our quitting 
  490.  **        flag to FALSE and installing our menus.
  491.  **}
  492.  
  493. {$S Initialize}
  494. PROCEDURE Initialize;
  495.  
  496. VAR
  497.     menuBar    : Handle;
  498.     
  499. BEGIN
  500.  
  501.     InitGraf(@thePort);
  502.     InitFonts;
  503.     InitWindows;
  504.     InitMenus;
  505.     TEInit;
  506.     InitDialogs(NIL);
  507.     InitCursor;
  508.     FlushEvents(everyEvent, 0);    
  509.  
  510.     gQuitting := FALSE;
  511.  
  512.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  513.     IF (menuBar = NIL) THEN ExitToShell;    {should do real error stuff here.}
  514.     SetMenuBar(menuBar);                    {install menus}
  515.     DisposHandle(menuBar);
  516.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  517.     DrawMenuBar;
  518.  
  519. END;    {**  Initialize  **}
  520.  
  521.  
  522. {$S _DataInit}
  523. PROCEDURE _DataInit; EXTERNAL;
  524.  
  525. {This routine is automatically linked in by the MPW Linker. This external
  526.  reference to it is done so that we can unload its segment, %A5Init.}
  527.  
  528.  
  529.  
  530. {*------ ToggleCMark ----------------------------------------------------------------*}
  531. {**
  532.  **        ToggleCMark is called when an item in the Settings menu is chosen. 
  533.  **        It simply toggles the checkmark next to the item passed.
  534.  **}
  535.  
  536. {$S Main}
  537. PROCEDURE ToggleCMark(menuItem: Integer);
  538.  
  539. VAR
  540.     setMHdl        : MenuHandle;
  541.     mark        : Char;
  542.  
  543. BEGIN
  544.  
  545.     setMHdl := GetMHandle(mSettings);
  546.     GetItemMark(setMHdl, menuItem, mark);
  547.     CheckItem(setMHdl, menuItem, (mark = Char(noMark)));
  548.  
  549. END;    {**  ToggleCMark  **}
  550.  
  551.  
  552. {*------ DoMenuCommand ----------------------------------------------------------------*}
  553. {**
  554.  **        DoMenuCommand is called when an item is chosen from the menu bar (after calling 
  555.  **        MenuSelect or MenuKey).  It does the right thing for each command.
  556.  **}
  557.  
  558. {$S Main}
  559. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  560.  
  561. VAR
  562.     menuID, menuItem    : INTEGER;
  563.     daRefNum            : INTEGER;
  564.     daName                : Str255;
  565.  
  566. BEGIN
  567.  
  568. {Get the menu ID and item ID.}
  569.  
  570.     menuID := HiWrd(menuResult);
  571.     menuItem := LoWrd(menuResult);
  572.  
  573.     CASE menuID OF
  574.         mApple:
  575.             CASE menuItem OF
  576.                 iAbout:                {bring up alert for About}
  577.                     (* We do nothing here... *);
  578.  
  579.                 OTHERWISE
  580.                 BEGIN        {all non-About items in this menu are DAs}
  581.                     GetItem(GetMHandle(mApple), menuItem, daName);
  582.                     daRefNum := OpenDeskAcc(daName);
  583.                 END;
  584.             END;
  585.  
  586.         mFile:                            {File Menu}
  587.             CASE menuItem OF
  588.                 iPrint:                        {-> Print Test Page.}
  589.                     PrintStuff;
  590.                 iQuit:
  591.                     gQuitting := TRUE;        {-> Quit}
  592.             END;
  593.  
  594.         mSettings:                        {Settings menu}
  595.             ToggleCMark(menuItem);            {-> Toggle checkmark for item.}
  596.     END;        
  597.     HiliteMenu(0);
  598.  
  599. END;    {**  DoMenuCommand  **}
  600.  
  601.  
  602. {*------ DoEvent ----------------------------------------------------------------*}
  603. {**
  604.  **        DoEvent handles incoming events for our app.  In this skimpy sample, we
  605.  **        only handle menu events and system clicks.
  606.  **}
  607.  
  608. {$S Main}
  609. PROCEDURE DoEvent;
  610.  
  611. VAR
  612.     part        : INTEGER;
  613.     key            : Char;
  614.     quitting    : Boolean;
  615.     event        : EventRecord;
  616.     window        : WindowPtr;
  617.  
  618. BEGIN
  619.  
  620. {Repeatedly handle menu selecting events until our quit flag is set.}
  621.  
  622.     REPEAT
  623.         BEGIN
  624.             SystemTask;                                    {This must be called if using GetNextEvent}
  625.  
  626.             IF (GetNextEvent(everyEvent, event)) THEN
  627.                 CASE event.what OF
  628.                     mouseDown:
  629.                         BEGIN
  630.                             part := FindWindow(event.where, window);
  631.                             CASE part OF
  632.                                 inMenuBar:
  633.                                     DoMenuCommand(MenuSelect(event.where));
  634.                 
  635.                                 inSysWindow:
  636.                                     SystemClick(event, window);
  637.                             END;
  638.                         END;
  639.     
  640.                     keyDown, autoKey:
  641.                         BEGIN
  642.                             key := CHR(BAnd(event.message, charCodeMask));
  643.                             IF (BAnd(event.modifiers, cmdKey) <> 0) AND (event.what = keyDown) THEN
  644.                                 DoMenuCommand(MenuKey(key));
  645.                         END;
  646.                 END;
  647.         END;
  648.     UNTIL gQuitting;
  649.  
  650. END;    {**  DoEvent  **}
  651.  
  652.  
  653. {*------ Main ----------------------------------------------------------------*}
  654. {**
  655.  **        Main kickstarts our app.
  656.  **}
  657.  
  658. {$S Main}
  659. BEGIN
  660.  
  661.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  662.     MaxApplZone;            {expand the heap so code segments load at the top}
  663.     Initialize;                {initialize the program}
  664.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  665.     DoEvent;                {handle menu events until quitting.}
  666.  
  667. END.    {**  HairLines.  **}
  668.  
  669.